﻿using GetSmartIDEList.Lib.AOP;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;

namespace GetSmartIDEList.Lib.IOC
{
    public class MyContainer:IContainer
    {
        private Dictionary<string, ContainerRegistModel> containerDictionary = new Dictionary<string, ContainerRegistModel>();
        private Dictionary<string, object[]> containerValueDictionary = new Dictionary<string, object[]>();

        /// <summary>
        /// 作用域单例的对象
        /// </summary>
        private Dictionary<string, object> containerScopeDictionary = new Dictionary<string, object>();

        private string GetKey(string fullName, string shortName) => $"{fullName}___{shortName}";


        public IContainer CreateChildContainer()
        {
            return new MyContainer(this.containerDictionary, this.containerValueDictionary, new Dictionary<string, object>());//没有注册关系,最好能初始化进去
        }
        public MyContainer() { }
        private MyContainer(Dictionary<string, ContainerRegistModel> containerDictionary,
            Dictionary<string, object[]> containerValueDictionary, Dictionary<string, object> containerScopeDictionary)
        {
            this.containerDictionary =containerDictionary;
            this.containerValueDictionary = containerValueDictionary;
            this.containerScopeDictionary = containerScopeDictionary;
        }


        /// <summary>
        /// 加个参数区分生命周期--而且注册关系得保存生命周期
        /// </summary>
        /// <typeparam name="TFrom"></typeparam>
        /// <typeparam name="TTo"></typeparam>
        /// <param name="shortName"></param>
        /// <param name="paraList"></param>
        public void Register<TFrom, TTo>(string shortName = null, object[] paraList = null, LifetimeType lifetimeType = LifetimeType.Transient) where TTo : TFrom
        {
            this.containerDictionary.Add(this.GetKey(typeof(TFrom).FullName, shortName), new ContainerRegistModel()
            {
                Lifetime = lifetimeType,
                TargetType = typeof(TTo)
            });
            if (paraList != null && paraList.Length > 0)
                this.containerValueDictionary.Add(this.GetKey(typeof(TFrom).FullName, shortName), paraList);
        }
        public void RegisterType(Type typeFrom, Type typeTo, LifetimeType lifetimeType = LifetimeType.Transient)
        {
            this.containerDictionary.Add(this.GetKey(typeFrom.FullName, ""), new ContainerRegistModel()
            {
                Lifetime = lifetimeType,
                TargetType = typeTo
            });

        }

        public TFrom Resolve<TFrom>(string shortName = null)
        {
            return (TFrom)this.ResolveObject(typeof(TFrom), shortName);
        }

        //private object oTestServiceA = null;
        /// <summary>
        /// 递归--可以完成不限层级的东西
        /// </summary>
        /// <typeparam name="TFrom"></typeparam>
        /// <returns></returns>
        private object ResolveObject(Type abstractType, string shortName = null)
        {
            string key = this.GetKey(abstractType.FullName, shortName);
            var model = this.containerDictionary[key];
            #region Lifetime
            switch (model.Lifetime)
            {
                case LifetimeType.Transient:
                    Console.WriteLine("Transient Do Nothing Before~~");
                    break;
                case LifetimeType.Singleton:
                    if (model.SingletonInstance == null)
                    {
                        break;
                    }
                    else
                    {
                        return model.SingletonInstance;
                    }
                case LifetimeType.Scope:
                    if (this.containerScopeDictionary.ContainsKey(key))
                    {
                        return this.containerScopeDictionary[key];
                    }
                    else
                    {
                        break;
                    }
                    break;
                case LifetimeType.PerThread:
                    //CallContext  Remoting一个本地线程数据存储  .NetCore没有
                    object oValue = CustomCallContext<object>.GetData($"{key}{Thread.CurrentThread.ManagedThreadId}");
                    if (oValue == null)
                    {
                        break;
                    }
                    else
                    {
                        return oValue;
                    }
                default:
                    break;
            }
            #endregion

            Type type = model.TargetType;

            ConstructorInfo ctor = null;
            //2 标记特性
            ctor = type.GetConstructors().FirstOrDefault(c => c.IsDefined(typeof(ConstructorAttribute), true));
            if (ctor == null)
            {
                //1 参数个数最多
                ctor = type.GetConstructors().OrderByDescending(c => c.GetParameters().Length).First();
            }

            #region 准备构造函数的参数
            List<object> paraList = new List<object>();

            object[] paraConstant = this.containerValueDictionary.ContainsKey(key) ? this.containerValueDictionary[key] : null;//常量找出来
            int iIndex = 0;
            foreach (var para in ctor.GetParameters())
            {
                if (para.IsDefined(typeof(ParameterConstantAttribute), true))
                {
                    paraList.Add(paraConstant[iIndex]);
                    iIndex++;
                }
                else
                {
                    Type paraType = para.ParameterType;//获取参数的类型 IUserDAL

                    string paraShortName = this.GetShortName(para);
                    object paraInstance = this.ResolveObject(paraType, paraShortName);
                    paraList.Add(paraInstance);
                }
            }
            #endregion

            object oInstance = null;
            oInstance = Activator.CreateInstance(type, paraList.ToArray());
        

            #region Lifetime
            switch (model.Lifetime)
            {
                case LifetimeType.Transient:
                    Console.WriteLine("Transient Do Nothing After~~");
                    break;
                case LifetimeType.Singleton:
                    model.SingletonInstance = oInstance;
                    break;
                case LifetimeType.Scope:
                    this.containerScopeDictionary[key] = oInstance;
                    break;
                case LifetimeType.PerThread:
                    CustomCallContext<object>.SetData($"{key}{Thread.CurrentThread.ManagedThreadId}", oInstance);
                    break;
                default:
                    break;
            }
            #endregion

            return oInstance.AOP(abstractType);
            //return oInstance;
        }

        private string GetShortName(ICustomAttributeProvider provider)
        {
            if (provider.IsDefined(typeof(ParameterShortNameAttribute), true))
            {
                var attribute = (ParameterShortNameAttribute)(provider.GetCustomAttributes(typeof(ParameterShortNameAttribute), true)[0]);
                return attribute.ShortName;
            }
            else
                return null;
        }

        public object Resolve(Type type)
        {
            return this.ResolveObject(type, null);
        }

    }
}
